#include "VolumeAlgorithm.h"
#include "OCVolume/VolumeEncoder.h"

namespace OCPP
{
	namespace Volume
	{
		inline StringVector mergeOutputName(const NcGridDataList& gridDataList)
		{
			StringVector sv = std::rbegin(gridDataList)->_dimensions;

			String name = *std::rbegin(sv);

			StringStream sstream;
			for (size_t i = 0; i < gridDataList.size() - 1; i++)
			{
				String name1 = *std::rbegin(gridDataList[i]._dimensions);
				for (size_t x = 0; x < name.size() && x < name1.size(); x++)
				{
					if (name1[x] != name[x])
					{
						sstream << name1[x];
					}
				}
				sstream << "_";
			}
			sstream << name;

			*std::rbegin(sv) = sstream.str();

			return sv;
		}

		VolumeGridData NcAlgorithmVecLength::onCompute(const NcGridDataList& gridDataList, const VolumeOptions& options)
		{
			assert(gridDataList.size() > 1);
			VolumeGridData  ncGridDataOut;
			ncGridDataOut.copyFrom(gridDataList[0]);
			ncGridDataOut.resetMissingValue();
			ncGridDataOut._dimensions = mergeOutputName(gridDataList);

			int dimZ = ncGridDataOut._VolumeExtent.dimZ;
			int dimY = ncGridDataOut._VolumeExtent.dimY;
			int dimX = ncGridDataOut._VolumeExtent.dimX;

			ncGridDataOut._vecFloat.resize(gridDataList[0].getSize());

			for (int z = 0; z < dimZ; z++)
			{
				for (int y = 0; y < dimY; y++)
				{
					for (int x = 0; x < dimX; x++)
					{
						int i = z * dimY * dimX + y * dimX + x;
						float value = 0.0f;
						for (const auto& output : gridDataList)
						{
							float v1 = output.getFloat(i);
							if (output.isValidValue(v1)) {
								value += (v1 * v1);
							}
						}
						value = OCPP::Math::Sqrt(value);
						ncGridDataOut._vecFloat[i] = value;
					}
				}
			}

			ncGridDataOut.setType(VolumeGridData::Type_Float);
			return ncGridDataOut;
		}

		VolumeGridData NcAlgorithmNone::onCompute(const NcGridDataList& gridDataList, const VolumeOptions& options)
		{
			size_t comp = gridDataList.size();
			assert(comp > 0);
			const auto& ncGridDataU = gridDataList[0];

			VolumeGridData  ncGridDataOut;
			ncGridDataOut.copyFrom(ncGridDataU);
			ncGridDataOut.resetMissingValue();
			ncGridDataOut._dimensions = mergeOutputName(gridDataList);

			int dimZ = ncGridDataOut._VolumeExtent.dimZ;
			int dimY = ncGridDataOut._VolumeExtent.dimY;
			int dimX = ncGridDataOut._VolumeExtent.dimX;

			size_t size = dimZ * dimY * dimX;
			assert(ncGridDataU.getSize() == size);

			ncGridDataOut._vecFloat.resize(size * comp);

			for (int z = 0; z < dimZ; z++)
			{
				for (int y = 0; y < dimY; y++)
				{
					for (int x = 0; x < dimX; x++)
					{
						int i = z * dimY * dimX + y * dimX + x;
						for (auto c = 0; c < comp; c++)
						{
							const auto& gridData = gridDataList[c];
							float valueU = gridData.getFloat(i);
							if (!gridData.isValidValue(valueU))
								valueU = 0.0f;
							ncGridDataOut._vecFloat[i * comp + c] = valueU;
						}
					}
				}
			}

			ncGridDataOut.setType(VolumeGridData::Type_Float);

			return ncGridDataOut;
		}

		VolumeGridData NcAlgorithmUV::onCompute(const NcGridDataList& gridDataList, const VolumeOptions& options)
		{
			assert(gridDataList.size() == 2);
			const auto& ncGridDataU = gridDataList[0];
			const auto& ncGridDataV = gridDataList[1];

			VolumeGridData  ncGridDataOut;
			ncGridDataOut.copyFrom(ncGridDataU);
			ncGridDataOut.resetMissingValue();
			ncGridDataOut._dimensions = mergeOutputName(gridDataList);

			int dimZ = ncGridDataOut._VolumeExtent.dimZ;
			int dimY = ncGridDataOut._VolumeExtent.dimY;
			int dimX = ncGridDataOut._VolumeExtent.dimX;

			size_t size = dimZ * dimY * dimX;
			assert(ncGridDataU.getSize() == size);
			assert(ncGridDataV.getSize() == size);

			ncGridDataOut._vecChar.resize(size * 4);

			for (int z = 0; z < dimZ; z++)
			{
				for (int y = 0; y < dimY; y++)
				{
					for (int x = 0; x < dimX; x++)
					{
						int i = z * dimY * dimX + y * dimX + x;
						float valueU = ncGridDataU.getFloat(i);
						float valueV = ncGridDataV.getFloat(i);
						if (!ncGridDataU.isValidValue(valueU))
							valueU = 0.0f;
						if (!ncGridDataV.isValidValue(valueV))
							valueV = 0.0f;
						auto rgba = VolumeEncoder::encodeUV(valueU, valueV);
						memcpy(&ncGridDataOut._vecChar[i * 4], &rgba[0], 4);
					}
				}
			}

			ncGridDataOut.setType(VolumeGridData::Type_Char);

			return ncGridDataOut;
		}

		VolumeGridData NcAlgorithmCR::onCompute(const NcGridDataList& gridDataList, const VolumeOptions& options)
		{
			assert(gridDataList.size() == 1);

			const auto& ncOutput = gridDataList[0];

			VolumeGridData  ncGridDataOut;
			ncGridDataOut.copyFrom(ncOutput);
			ncGridDataOut._dimensions = ncOutput._dimensions;
			*std::rbegin(ncGridDataOut._dimensions) += "_CR";
			int dimZ = ncGridDataOut._VolumeExtent.dimZ;
			int dimY = ncGridDataOut._VolumeExtent.dimY;
			int dimX = ncGridDataOut._VolumeExtent.dimX;
			int dimYX = dimY * dimX;

			ncGridDataOut._vecFloat.resize(dimYX);

			for (int z = 0; z < dimZ; z++)
			{
				int iZ = z * dimY * dimX;
				for (int y = 0; y < dimY; y++)
				{
					int iY = y * dimX;
					for (int x = 0; x < dimX; x++)
					{
						int iOut = iY + x;
						int i = iZ + iY + x;
						float value = ncGridDataOut._vecFloat[iOut];
						float v1 = ncOutput.getFloat(i);
						if (ncOutput.isValidValue(v1) && v1 > value) {
							ncGridDataOut._vecFloat[iOut] = v1;
						}
					}
				}
			}

			ncGridDataOut.resetMissingValue();
			ncGridDataOut._VolumeExtent.dimZ = 1;

			ncGridDataOut.setType(VolumeGridData::Type_Float);
			return ncGridDataOut;
		}
	}
}


